﻿using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using Notoric.Commanding;
using System.Windows.Input;

namespace Notoric.View.Music.CWNotation.Controls
{
    /// <summary>
    /// Visualizes a clef.
    /// </summary>
    public partial class Clef : UserControl
    {
        #region Constructors

        /// <summary>
        /// Creates a new <see cref="Clef"/> instance.
        /// </summary>
        public Clef()
        {
            InitializeComponent();
        }

        #endregion

        #region Event Handlers

        /// <summary>
        /// Occurs when the contol has been loaded.
        /// </summary>
        /// <remarks>
        /// <b>Operation:</b><br/>
        /// The handler caches the visualized clef, binds to its <see cref="ViewModel.ViewModelElement.PropertyChanged"/> event
        /// and calls <see cref="SetVisualization"/>.
        /// </remarks>
        /// <param name="sender">Object where the event handler is attached</param>
        /// <param name="e">Event data</param>
        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            if (DataContext != null)
            {
                clef = (ViewModel.Elements.CWNotation.Clef)DataContext;
                clef.PropertyChanged += new PropertyChangedEventHandler(Clef_PropertyChanged);

                SetVisualization();
            }
        }

        /// <summary>
        /// Occurs when the data context (visualized clef) has changed.
        /// </summary>
        /// <remarks>
        /// <b>Operation:</b><br/>
        /// The handler unbinds from the old clef's <see cref="ViewModel.ViewModelElement.PropertyChanged"/>, 
        /// caches the new visualized clef, binds to its <see cref="ViewModel.ViewModelElement.PropertyChanged"/> event
        /// and calls <see cref="SetVisualization"/>.
        /// </remarks>
        /// <param name="sender">Object where the event handler is attached</param>
        /// <param name="e">Event data</param>
        private void UserControl_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (clef != null)
                clef.PropertyChanged -= Clef_PropertyChanged;

            clef = (ViewModel.Elements.CWNotation.Clef)DataContext;

            if (clef != null)
            {
                clef.PropertyChanged += new PropertyChangedEventHandler(Clef_PropertyChanged);
                SetVisualization();
            }
        }

        /// <summary>
        /// Occurs when the value of some of the clef's properties has changed.
        /// </summary>
        /// <remarks>
        /// <b>Operation:</b><br/>
        /// If the modified property is <see cref="Notoric.ViewModel.Elements.CWNotation.Clef.Type"/>,
        /// the handler calls the <see cref="SetVisualization"/> method.
        /// </remarks>
        /// <param name="sender">Object that has sent the event</param>
        /// <param name="e">Event data</param>
        private void Clef_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Type")
                SetVisualization();
        }

        /// <summary>
        /// Handles the Click event of the menu_GClef control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void menu_ClefType_Click(object sender, RoutedEventArgs e)
        {
            var menuItem = (MenuItem)sender;
            ViewModel.Elements.CWNotation.ClefType clefType;

            switch (menuItem.Name)
            {
                case "menu_GClef":
                    clefType = ViewModel.Elements.CWNotation.ClefType.G;
                    break;
                case "menu_FClef":
                    clefType = ViewModel.Elements.CWNotation.ClefType.F;
                    break;
                case "menu_CClef":
                    clefType = ViewModel.Elements.CWNotation.ClefType.C;
                    break;
                default:
                    return;
            }

            if (clef != null)
            {
                var cmd = clef.CreateSetTypeCommand(clefType);
                cmd.Execute(null);
            }
        }

        /// <summary>
        /// Handles the Click event of the menu_CombinedClefType control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void menu_CombinedClefType_Click(object sender, RoutedEventArgs e)
        {
            var menuItem = (MenuItem)sender;
            ViewModel.Elements.CWNotation.ClefType clefType;
            int staffPosition = 2;

            switch (menuItem.Name)
            {
                case "menu_TrebleClef":
                    clefType = ViewModel.Elements.CWNotation.ClefType.G;
                    staffPosition = 2;
                    break;
                case "menu_BassClef":
                    clefType = ViewModel.Elements.CWNotation.ClefType.F;
                    staffPosition = 6;
                    break;
                case "menu_TenorClef":
                    clefType = ViewModel.Elements.CWNotation.ClefType.C;
                    staffPosition = 6;
                    break;
                case "menu_AltoClef":
                    clefType = ViewModel.Elements.CWNotation.ClefType.C;
                    staffPosition = 4;
                    break;
                default:
                    return;
            }

            if (clef != null)
            {
                var cmdType = clef.CreateSetTypeCommand(clefType);
                var cmdPos = clef.CreateSetStaffPositionCommand(staffPosition);
                var macro = new UndoableMacroCommand(clef.UndoStack, clef.RedoStack)
                {
                    Name = cmdType.Name
                };

                macro.Commands.Add(cmdType);
                macro.Commands.Add(cmdPos);
                macro.Execute(null);
            }
        }

        /// <summary>
        /// Handles the PreviewMouseLeftButtonDown event of the UserControl control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param>
        private void UserControl_PreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            if (Keyboard.IsKeyUp(Key.LeftCtrl) && Keyboard.IsKeyUp(Key.RightCtrl))
                clef.FindOwner<ViewModel.Elements.CWNotation.MusicSectionInterior>().ClearSelection();
            clef.FindOwner<ViewModel.Elements.CWNotation.MusicSectionInterior>().AddToSelection(clef);
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Selects the symbol and margin for the <see cref="PART_ClefSymbol"/> control
        /// to visualize the clef's type.
        /// </summary>
        private void SetVisualization()
        {
            var staffSpaces = clef.FindOwner<ViewModel.Elements.CWNotation.Staff>().Lines.Count - 1;
            var spaceSize = (staffSpaces > 0 ? clef.EffectiveTypeSize / staffSpaces : clef.FindOwner<ViewModel.Elements.CWNotation.Staff>().SpaceSize);

            switch (clef.Type)
            {
                case ViewModel.Elements.CWNotation.ClefType.G:
                    PART_ClefSymbol.Text = "&";
                    PART_ClefSymbol.Margin = new Thickness(0, - 4.5 * spaceSize, 0, 0);
                    break;
                case ViewModel.Elements.CWNotation.ClefType.F:
                    PART_ClefSymbol.Text = "?";
                    PART_ClefSymbol.Margin = new Thickness(0, - 2.5 * spaceSize, 0, 0);
                    break;
                case ViewModel.Elements.CWNotation.ClefType.C:
                    PART_ClefSymbol.Text = "B";
                    PART_ClefSymbol.Margin = new Thickness(0, -3.5 * spaceSize, 0, 0);
                    break;
            }
        }

        #endregion

        #region Private Fields

        /// <summary>
        /// Holds a reference to the visualized clef.
        /// </summary>
        private ViewModel.Elements.CWNotation.Clef clef;

        #endregion
    }
}
